"""
Functions for reading NEXRAD Level 3 products.

"""

import numpy as np

from ..config import FileMetadata, get_fillvalue
from ..core.radar import Radar
from .common import make_time_unit_str, _test_arguments, prepare_for_read
from .nexrad_level3 import NEXRADLevel3File


def read_nexrad_level3(filename, field_names=None, additional_metadata=None,
                       file_field_names=False, exclude_fields=None,
                       include_fields=None, **kwargs):
    """
    Read a NEXRAD Level 3 product.

    Parameters
    ----------
    filename : str
        Filename of NEXRAD Level 3 product file. The files hosted by
        at the NOAA National Climate Data Center [1]_ as well as on the
        NWS WSR-88D Level III Data Collection and Distribution Network
        have been tests. Other NEXRAD Level 3 files may or may not work.
        A file-like object pointing to the beginning of such a file is also
        supported [2]_.
    field_names : dict, optional
        Dictionary mapping NEXRAD level 3 product number to radar field names.
        If the product number of the file does not appear in this dictionary
        or has a value of None it will not be placed in the radar.fields
        dictionary. A value of None, the default, will use the mapping
        defined in the metadata configuration file.
    additional_metadata : dict of dicts, optional
        Dictionary of dictionaries to retrieve metadata from during this read.
        This metadata is not used during any successive file reads unless
        explicitly included. A value of None, the default, will not
        introduct any addition metadata and the file specific or default
        metadata as specified by the metadata configuration file will be used.
    file_field_names : bool, optional
        True to use the product number for the field name. In this case the
        field_names parameter is ignored. The field dictionary will likely
        only have a 'data' key, unless the fields are defined in
        `additional_metadata`.
    exclude_fields : list or None, optional
        List of fields to exclude from the radar object. This is applied
        after the `file_field_names` and `field_names` parameters. Set
        to None to include all fields specified by include_fields.
    include_fields : list or None, optional
        List of fields to include from the radar object. This is applied
        after the `file_field_names` and `field_names` parameters. Set
        to None to include all fields not specified by exclude_fields.

    Returns
    -------
    radar : Radar
        Radar object containing all moments and sweeps/cuts in the volume.
        Gates not collected are masked in the field data.

    References
    ----------
    .. [1] http://www.ncdc.noaa.gov/
    .. [2] http://www.roc.noaa.gov/wsr88d/Level_III/Level3Info.asp

    """
    # test for non empty kwargs
    _test_arguments(kwargs)

    # create metadata retrieval object
    filemetadata = FileMetadata('nexrad_level3', field_names,
                                additional_metadata, file_field_names,
                                exclude_fields, include_fields)

    # open the file
    nfile = NEXRADLevel3File(prepare_for_read(filename))
    nradials = nfile.packet_header['nradials']
    msg_code = nfile.msg_header['code']

    # time
    time = filemetadata('time')
    time_start = nfile.get_volume_start_datetime()
    time['units'] = make_time_unit_str(time_start)
    time['data'] = np.zeros((nradials, ), dtype='float64')

    # range
    _range = filemetadata('range')
    _range['data'] = nfile.get_range()
    _range['meters_to_center_of_first_gate'] = _range['data'][0]
    _range['meters_between_gates'] = _range['data'][1] - _range['data'][0]

    # fields
    fields = {}
    field_name = filemetadata.get_field_name(msg_code)
    if field_name is None:
        fields = {}
    else:
        dic = filemetadata(field_name)
        dic['_FillValue'] = get_fillvalue()
        dic['data'] = nfile.get_data()
        fields = {field_name: dic}

    # metadata
    metadata = filemetadata('metadata')
    metadata['original_container'] = 'NEXRAD Level 3'

    # scan_type
    scan_type = 'ppi'

    # latitude, longitude, altitude
    latitude = filemetadata('latitude')
    longitude = filemetadata('longitude')
    altitude = filemetadata('altitude')

    lat, lon, height = nfile.get_location()
    # Nexrad altitude is in feet, convert to meters unless user's
    # default config has units in feet.
    if altitude['units'] == 'meters':
        height = height * 0.3048
    latitude['data'] = np.array([lat], dtype='float64')
    longitude['data'] = np.array([lon], dtype='float64')
    altitude['data'] = np.array([height], dtype='float64')

    # sweep_number, sweep_mode, fixed_angle, sweep_start_ray_index
    # sweep_end_ray_index
    sweep_number = filemetadata('sweep_number')
    sweep_mode = filemetadata('sweep_mode')
    sweep_start_ray_index = filemetadata('sweep_start_ray_index')
    sweep_end_ray_index = filemetadata('sweep_end_ray_index')

    sweep_number['data'] = np.array([0], dtype='int32')
    sweep_mode['data'] = np.array(1 * ['azimuth_surveillance'], dtype='S')

    sweep_start_ray_index['data'] = np.array([0], dtype='int32')
    sweep_end_ray_index['data'] = np.array([nradials - 1], dtype='int32')

    # azimuth, elevation, fixed_angle
    azimuth = filemetadata('azimuth')
    elevation = filemetadata('elevation')
    fixed_angle = filemetadata('fixed_angle')
    azimuth['data'] = nfile.get_azimuth()
    elev = nfile.get_elevation()
    elevation['data'] = np.ones((nradials, ), dtype='float32') * elev
    fixed_angle['data'] = np.array([elev], dtype='float32')

    nfile.close()
    return Radar(
        time, _range, fields, metadata, scan_type,
        latitude, longitude, altitude,
        sweep_number, sweep_mode, fixed_angle, sweep_start_ray_index,
        sweep_end_ray_index,
        azimuth, elevation,
        instrument_parameters=None)
